package org.example.utils;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.springframework.web.multipart.MultipartFile;

import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;

/**
 * @author liuyunzhe
 * @version 1.0
 * @description: http工具类
 * @date 2023/5/20 11:21
 */
public class HttpUtils {
    private static final String CHARSET = "utf-8";
    private static final String APPLICATION_FORM_URLENCODED = "application/x-www-form-urlencoded; charset=utf-8";
    // private static final int DEFAULT_CONNECT_TIMEOUT = 1000;
    // private static final int DEFAULT_READ_TIMEOUT = 3000;
    private static final ThreadLocal<Integer> CONNECT_TIMEOUT = new ThreadLocal();
    private static final ThreadLocal<Integer> READ_TIMEOUT = new ThreadLocal();

    private static final ContentType contentType = ContentType.create("multipart/form-data", Charset.forName("UTF-8"));//重新设置UTF-8编码，默认编码是ISO-8859-1


    /**
     * 发送get请求
     *
     * @param url     请求链接
     * @param headers 请求头
     * @param querys  请求参数
     * @return
     * @throws Exception
     */
    public static HttpResponse doGet(String url, Map<String, String> headers, Map<String, String> querys) throws Exception {
        HttpClient httpClient = wrapClient(url);
        HttpGet request = new HttpGet(buildUrl(url, querys));
        setConfig(request);
        setHeaders(request, headers);
        HttpResponse response = httpClient.execute(request);
        return response;
    }

    /**
     * 发送post请求
     *
     * @param url     请求链接
     * @param headers 请求头
     * @param querys  请求参数
     * @param bodys   请求体
     * @return
     * @throws Exception
     */
    public static HttpResponse doPost(String url, Map<String, String> headers, Map<String, String> querys, Map<String, String> bodys) throws Exception {
        HttpClient httpClient = wrapClient(url);
        HttpPost request = new HttpPost(buildUrl(url, querys));
        setConfig(request);
        setHeaders(request, headers);
        if (MapUtils.isNotEmpty(bodys)) {
            List<NameValuePair> nameValuePairList = (List) bodys.entrySet().stream().map((entry) -> {
                return new BasicNameValuePair((String) entry.getKey(), (String) entry.getValue());
            }).collect(Collectors.toList());
            UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nameValuePairList, "utf-8");
            formEntity.setContentType("application/x-www-form-urlencoded; charset=utf-8");
            request.setEntity(formEntity);
        }

        HttpResponse response = httpClient.execute(request);
        return response;
    }

    /**
     * @param url     请求url
     * @param headers 请求头
     * @param querys  请求参数
     * @param body    请求体 json字符串
     * @return
     * @throws Exception
     */
    public static HttpResponse doPost(String url, Map<String, String> headers, Map<String, String> querys, String body) throws Exception {
        HttpClient httpClient = wrapClient(url);
        HttpPost request = new HttpPost(buildUrl(url, querys));
        setConfig(request);
        setHeaders(request, headers);
        if (StringUtils.isNotBlank(body)) {
            request.setEntity(new StringEntity(body, "utf-8"));
        }

        HttpResponse response = httpClient.execute(request);
        return response;
    }

    /**
     * @param url
     * @param headers
     * @param querys
     * @param body    数据流
     * @return
     * @throws Exception
     */
    public static HttpResponse doPost(String url, Map<String, String> headers, Map<String, String> querys, byte[] body) throws Exception {
        HttpClient httpClient = wrapClient(url);
        HttpPost request = new HttpPost(buildUrl(url, querys));
        setConfig(request);
        setHeaders(request, headers);
        if (ArrayUtils.isNotEmpty(body)) {
            request.setEntity(new ByteArrayEntity(body));
        }

        HttpResponse response = httpClient.execute(request);
        return response;
    }

    public static HttpResponse doPut(String url, Map<String, String> headers, Map<String, String> querys, Map<String, String> bodys) throws Exception {
        HttpClient httpClient = wrapClient(url);
        HttpPut request = new HttpPut(buildUrl(url, querys));
        setConfig(request);
        setHeaders(request, headers);
        if (MapUtils.isNotEmpty(bodys)) {
            List<NameValuePair> nameValuePairList = (List) bodys.entrySet().stream().map((entry) -> {
                return new BasicNameValuePair((String) entry.getKey(), (String) entry.getValue());
            }).collect(Collectors.toList());
            UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nameValuePairList, "utf-8");
            formEntity.setContentType("application/x-www-form-urlencoded; charset=utf-8");
            request.setEntity(formEntity);
        }

        HttpResponse response = httpClient.execute(request);
        return response;
    }

    /**
     * put请求
     *
     * @param url
     * @param headers
     * @param querys
     * @param body    json字符串
     * @return
     * @throws Exception
     */
    public static HttpResponse doPut(String url, Map<String, String> headers, Map<String, String> querys, String body) throws Exception {
        HttpClient httpClient = wrapClient(url);
        HttpPut request = new HttpPut(buildUrl(url, querys));
        setConfig(request);
        setHeaders(request, headers);
        if (StringUtils.isNotBlank(body)) {
            request.setEntity(new StringEntity(body, "utf-8"));
        }

        HttpResponse response = httpClient.execute(request);
        return response;
    }

    /**
     * put 请求
     *
     * @param url
     * @param headers
     * @param querys
     * @param body    数据流
     * @return
     * @throws Exception
     */
    public static HttpResponse doPut(String url, Map<String, String> headers, Map<String, String> querys, byte[] body) throws Exception {
        HttpClient httpClient = wrapClient(url);
        HttpPut request = new HttpPut(buildUrl(url, querys));
        setConfig(request);
        setHeaders(request, headers);
        if (ArrayUtils.isNotEmpty(body)) {
            request.setEntity(new ByteArrayEntity(body));
        }

        HttpResponse response = httpClient.execute(request);
        return response;
    }

    /**
     * 删除请求
     *
     * @param url
     * @param headers
     * @param querys  请求参数
     * @return
     * @throws Exception
     */
    public static HttpResponse doDelete(String url, Map<String, String> headers, Map<String, String> querys) throws Exception {
        HttpClient httpClient = wrapClient(url);
        HttpDelete request = new HttpDelete(buildUrl(url, querys));
        setConfig(request);
        setHeaders(request, headers);
        HttpResponse response = httpClient.execute(request);
        return response;
    }

    /**
     * @param url     请求的url
     * @param headers 请求头
     * @param querys  查询参数 会把参数拼接到链接后面
     * @param bodys   请求体的形式传输
     * @param files   文件
     * @return
     * @throws Exception
     */
    public static HttpResponse doPost(String url, Map<String, String> headers, Map<String, String> querys, Map<String, Object> bodys, List<Map<String, ByteArrayMultipartFile>> files) throws Exception {
        HttpClient httpClient = wrapClient(url);
        HttpPost request = new HttpPost(buildUrl(url, querys));
        setConfig(request);
        setHeaders(request, headers);
        MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
        multipartEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
        if (MapUtils.isNotEmpty(bodys)) {
            bodys.entrySet().forEach((entry) -> {
                //在这里只处理 integer double  String  Float 类型
                if (entry.getValue() instanceof Integer
                        || entry.getValue() instanceof Float
                        || entry.getValue() instanceof Double
                        || entry.getValue() instanceof String) {
                    multipartEntityBuilder.addTextBody(entry.getKey(), String.valueOf(entry.getValue()), contentType);
                }
            });
        }
        if (CollectionUtils.isNotEmpty(files)) {
            files.forEach(map -> {
                map.entrySet().stream()
                        .forEach(entrySet -> {
                            try {
                                if (entrySet.getValue() != null) {
                                    multipartEntityBuilder.addBinaryBody(entrySet.getKey(), entrySet.getValue().getBytes(), contentType, entrySet.getValue().getName());
                                }
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            }
                        });
            });
        }
        HttpEntity reqEntity = multipartEntityBuilder.setCharset(Charset.forName("utf-8")).build();
        request.setEntity(reqEntity);
        HttpResponse response = httpClient.execute(request);
        return response;
    }

    /**
     * @param url
     * @param headers
     * @param querys
     * @param bodys
     * @param files   如果是通过接口上传文件，那么File类型为StandardMultipartFile
     * @return
     * @throws Exception
     */
    public static HttpResponse doPostForMuiltPartFile(String url, Map<String, String> headers, Map<String, String> querys, Map<String, Object> bodys, List<Map<String, MultipartFile>> files) throws Exception {
        HttpClient httpClient = wrapClient(url);
        HttpPost request = new HttpPost(buildUrl(url, querys));
        setConfig(request);
        setHeaders(request, headers);
        MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
        multipartEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
        if (MapUtils.isNotEmpty(bodys)) {
            bodys.entrySet().forEach((entry) -> {
                if (entry.getValue() instanceof Integer
                        || entry.getValue() instanceof Float
                        || entry.getValue() instanceof Double
                        || entry.getValue() instanceof String
                        || entry.getValue() instanceof BigDecimal) {
                    multipartEntityBuilder.addTextBody(entry.getKey(), String.valueOf(entry.getValue()), contentType);
                }
            });
        }
        if (CollectionUtils.isNotEmpty(files)) {
            files.forEach(map -> {
                map.entrySet().stream()
                        .forEach(entrySet -> {
                            try {
                                if (entrySet.getValue() != null) {
                                    multipartEntityBuilder.addBinaryBody(entrySet.getKey(), entrySet.getValue().getBytes(), contentType, entrySet.getValue().getOriginalFilename());
                                }
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            }
                        });
            });
        }
        HttpEntity reqEntity = multipartEntityBuilder.setCharset(Charset.forName("utf-8")).build();
        request.setEntity(reqEntity);
        HttpResponse response = httpClient.execute(request);
        return response;
    }

    //配置超时时间
    public static void configTimeout(int connectTimeout, int readTimeout) {
        CONNECT_TIMEOUT.set(connectTimeout);
        READ_TIMEOUT.set(readTimeout);
    }

    /**
     * 设置超时时间
     *
     * @param request
     */
    private static void setConfig(HttpRequestBase request) {
        Integer connectTimeout = (Integer) CONNECT_TIMEOUT.get();
        Integer readTimeout = (Integer) READ_TIMEOUT.get();
        RequestConfig config = RequestConfig.custom().setConnectTimeout(connectTimeout != null ? connectTimeout : 3000).setSocketTimeout(readTimeout != null ? readTimeout : 3000).build();
        request.setConfig(config);
    }

    /**
     * 设置请求头
     *
     * @param request
     * @param headers
     */
    private static void setHeaders(HttpRequestBase request, Map<String, String> headers) {
        if (MapUtils.isNotEmpty(headers)) {
            headers.entrySet().forEach((entry) -> {
                request.addHeader((String) entry.getKey(), (String) entry.getValue());
            });
        }

    }

    private static CloseableHttpClient wrapClient(String url) {
        HttpClientBuilder clientBuilder = HttpClientBuilder.create();
        if (url.startsWith("https://")) {
            clientBuilder.setSSLContext(createSSLContext()).setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
        }

        CloseableHttpClient httpClient = clientBuilder.build();
        return httpClient;
    }

    private static SSLContext createSSLContext() {
        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("TLS");
            X509TrustManager trustManager = new X509TrustManager() {
                public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException {
                }

                public void checkServerTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException {
                }

                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };
            sslContext.init((KeyManager[]) null, new TrustManager[]{trustManager}, (SecureRandom) null);
            return sslContext;
        } catch (Exception var2) {
            throw new RuntimeException(var2);
        }
    }

    private static String buildUrl(String url, Map<String, String> querys) throws UnsupportedEncodingException {
        StringBuffer sbUrl = new StringBuffer();
        sbUrl.append(url.endsWith("/") ? url.substring(0, url.length() - 1) : url);
        if (MapUtils.isNotEmpty(querys)) {
            StringBuffer sbQuery = new StringBuffer();
            Iterator var4 = querys.entrySet().iterator();

            while (var4.hasNext()) {
                Entry<String, String> query = (Entry) var4.next();
                if (sbQuery.length() > 0) {
                    sbQuery.append("&");
                }
                if (StringUtils.isBlank((CharSequence) query.getKey()) && StringUtils.isNotBlank((CharSequence) query.getValue())) {
                    sbQuery.append((String) query.getValue());
                }

                if (StringUtils.isNotBlank((CharSequence) query.getKey())) {
                    sbQuery.append((String) query.getKey());
                    if (StringUtils.isNotBlank((CharSequence) query.getValue())) {
                        sbQuery.append("=").append(URLEncoder.encode((String) query.getValue(), "utf-8"));
                    }
                }
            }
            if (sbQuery.length() > 0) {
                sbUrl.append(url.contains("?") ? "&" : "?").append(sbQuery);
            }
        }
        url = sbUrl.toString();
        return url;
    }

}
